/*
 * TabMetodos.cpp
 *
 *  Created on: 02/06/2013
 *      Author: juan
 */

#include <sstream>

#include "gui/SubWindow/ClassInfoWindow/TabMetodos.h"
#include "model/clases/Parametro.h"


TabMetodos::TabMetodos():
    frameEdicionMetodos("Propiedades método"),
    nombreMetodo("Nombre: "),
    tipoMetodo("Tipo: "),
    metodoPublico("Público"),
    metodoPrivado("Privado"),
    metodoProtegido("Protegido"),
    frameEdicionPrametros("Propiedades parámetros"),
    nombreParametro("Nombre: "),
    tipoParametro("Tipo: "),
    valorParametro("Valor: "),
    metodo_seleccionado(false),
    parametro_seleccionado(false),
    numeroParametro(1),
    numeroMetodo(1),
    eliminarMetodos(true){


    this->add(cajaPrincipal);

    cajaPrincipal.pack_start(cajaListaMetodos,Gtk::PACK_EXPAND_WIDGET,5);
    cajaPrincipal.pack_start(frameEdicionMetodos,Gtk::PACK_SHRINK,5);

    cajaListaMetodos.pack_start(ventanaScrolleableMetodos,Gtk::PACK_EXPAND_WIDGET);
    cajaListaMetodos.pack_start(cajaBotonesControlListaMetodos,Gtk::PACK_SHRINK);

    ventanaScrolleableMetodos.add(treeViewMetodos);
    ventanaScrolleableMetodos.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

    treeModelMetodos = Gtk::TreeStore::create(columnasMetodos);
    treeViewMetodos.set_model(treeModelMetodos);
    treeViewMetodos.append_column("Visibilidad", columnasMetodos.visibilidad);
    treeViewMetodos.append_column("Tipo", columnasMetodos.tipo);
    treeViewMetodos.append_column("Nombre", columnasMetodos.nombre);
    treeViewMetodos.append_column("Parametros", columnasMetodos.parametros);


    cajaBotonesControlListaMetodos.pack_start(botonNuevoMetodos,Gtk::PACK_SHRINK);
    cajaBotonesControlListaMetodos.pack_start(botonEliminarMetodos,Gtk::PACK_SHRINK);

    botonNuevoMetodos.set_label("Nuevo");
    botonNuevoMetodos.signal_clicked().connect(sigc::mem_fun(this, &TabMetodos::on_button_NuevoMetodo_clicked));
    botonEliminarMetodos.set_label("Eliminar");
    botonEliminarMetodos.signal_clicked().connect(sigc::mem_fun(this, &TabMetodos::on_button_EliminarMetodo_clicked));



    frameEdicionMetodos.add(cajaEdicionMetodos);
    cajaEdicionMetodos.pack_start(cajaPropiedadesMetodos, Gtk::PACK_SHRINK, 2);
    cajaEdicionMetodos.pack_start(frameEdicionPrametros, Gtk::PACK_SHRINK, 2);

    cajaPropiedadesMetodos.pack_start(cajaPropiedadesTextoMetodo,Gtk::PACK_SHRINK);
    cajaPropiedadesMetodos.pack_start(cajaVisivilidadMetodo,Gtk::PACK_SHRINK);


    cajaPropiedadesTextoMetodo.pack_start(*(nombreMetodo.getContenedor()),Gtk::PACK_SHRINK, 2);
    cajaPropiedadesTextoMetodo.pack_start(*(tipoMetodo.getContenedor()),Gtk::PACK_SHRINK, 2);

    cajaVisivilidadMetodo.pack_start(metodoPublico, Gtk::PACK_SHRINK, 2);
    cajaVisivilidadMetodo.pack_start(metodoPrivado, Gtk::PACK_SHRINK, 2);
    cajaVisivilidadMetodo.pack_start(metodoProtegido, Gtk::PACK_SHRINK, 2);

    metodoPublico.set_active();

    metodoPublico.set_group(grupoMetdodo);
    metodoPrivado.set_group(grupoMetdodo);
    metodoProtegido.set_group(grupoMetdodo);


    frameEdicionPrametros.add(cajaEdicionParametros);

    cajaEdicionParametros.pack_start(ventanaScrolleableParametros,Gtk::PACK_EXPAND_WIDGET, 2);
    cajaEdicionParametros.pack_start(cajaBotonesControlListaParametros,Gtk::PACK_SHRINK, 2);
    cajaEdicionParametros.pack_start(cajaEdicionParametro,Gtk::PACK_SHRINK, 2);


    ventanaScrolleableParametros.add(treeViewParametros);
    ventanaScrolleableParametros.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

    treeModelParametros = Gtk::TreeStore::create(columnasParametros);
    treeViewParametros.set_model(treeModelParametros);
    treeViewParametros.append_column("Tipo", columnasParametros.tipo);
    treeViewParametros.append_column("Nombre", columnasParametros.nombre);
    treeViewParametros.append_column("Valor", columnasParametros.valor);

    cajaBotonesControlListaParametros.pack_start(nuevoParametro,Gtk::PACK_SHRINK);
    cajaBotonesControlListaParametros.pack_start(eliminarParametro,Gtk::PACK_SHRINK);

    nuevoParametro.set_label("Nuevo");
    nuevoParametro.signal_clicked().connect(sigc::mem_fun(this, &TabMetodos::on_button_NuevoParametro_clicked));
    eliminarParametro.set_label("Eliminar");
    eliminarParametro.signal_clicked().connect(sigc::mem_fun(this, &TabMetodos::on_button_EliminarParametro_clicked));


    cajaEdicionParametro.pack_start(*(nombreParametro.getContenedor()),Gtk::PACK_SHRINK, 2);
    cajaEdicionParametro.pack_start(*(tipoParametro.getContenedor()),Gtk::PACK_SHRINK, 2);
    cajaEdicionParametro.pack_start(*(valorParametro.getContenedor()),Gtk::PACK_SHRINK, 2);

    desactivar_entradas_metodos();
}

TabMetodos::~TabMetodos() {

    if(eliminarMetodos){
        std::map<std::string,Metodo*> ::iterator it;

        for(it = metodos.begin();it != metodos.end();it++){
            delete (it->second);
        }
    }
}

void TabMetodos::insertarse(Gtk::Notebook& m_Notebook){
    m_Notebook.append_page(*this, "Métodos");
}

void TabMetodos::inicializarMetodos(std::vector<Metodo*> metodos){

    unsigned i;

    for(i = 0; i < metodos.size() ; i++)
    {
        Gtk::TreeModel::Row row = *(treeModelMetodos->append());
        row[columnasMetodos.nombre] = metodos[i]->getNombre();
        row[columnasMetodos.tipo] = metodos[i]->getTipoDatoDevuelto();
        row[columnasMetodos.parametros] = generarListaParametros(metodos[i]->getParametros());
        row[columnasMetodos.visibilidad] = Utils::visivilidadEnumToString[metodos[i]->getVisibilidad()];;

        std::stringstream ss;
        //La firma de un metodo es su nombre mas sus parametros
        //Si cambia alguno hay que editar el mapa
        ss << row[columnasMetodos.nombre] << row[columnasMetodos.parametros];

        this->metodos[ss.str()] = new Metodo(*metodos[i]);
    }

    treeViewMetodos.signal_row_activated().connect(sigc::mem_fun(this, &TabMetodos::on_row_method_activated));
    treeViewParametros.signal_row_activated().connect(sigc::mem_fun(this, &TabMetodos::on_row_param_activated));
}

std::string TabMetodos::generarListaParametros(std::vector<Parametro*> parametros){
    std::stringstream ss;
    unsigned i;

    ss << "( ";

    for(i = 0 ; i < parametros.size() ; i++)
    {
        ss << parametros[i]->getTipoDato() << " ";
        ss << parametros[i]->getNombre();

        if(parametros[i]->getValor().size() > 0){
            ss << " = " << parametros[i]->getValor();
        }

        if((i+1) < parametros.size())
        {
            ss << " , ";
        }
    }


    ss << ")";
    return ss.str();
}

void TabMetodos::on_row_method_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){

    Gtk::TreeModel::Row row =  *treeModelMetodos->get_iter(path);
    std::stringstream ss;
    std::stringstream temporal;
    unsigned i;
    std::vector<Parametro*> parametros;

    if(metodo_seleccionado){
        actualizar_metodo();
    }else{
        activar_entradas_metodos();
    }

    nombreMetodo.getEntry()->set_text(row[columnasMetodos.nombre]);
    tipoMetodo.getEntry()->set_text(row[columnasMetodos.tipo]);


    temporal << row[columnasMetodos.visibilidad];

    switch(Utils::visivilidadStringToEnum[temporal.str()]){
    case PRIVATE:
        metodoPrivado.set_active();
        break;
    case PUBLIC:
        metodoPublico.set_active();
        break;
    case PROTECTED:
        metodoProtegido.set_active();
        break;
    }



    std::map<std::string,Metodo*>::iterator it;

    ss << row[columnasMetodos.nombre] << row[columnasMetodos.parametros];
    it = metodos.find(ss.str());
    treeModelParametros->clear();

    if(it != metodos.end())
    {
        parametros = (it->second)->getParametros();


        for(i = 0; i < parametros.size() ;  i++)
        {
            Gtk::TreeModel::Row row = *(treeModelParametros->append());
            row[columnasParametros.nombre] = parametros[i]->getNombre();
            row[columnasParametros.tipo] = parametros[i]->getTipoDato();
            row[columnasParametros.valor] = parametros[i]->getValor();
        }
    }

    metodo_seleccionado = true;
    row_metodo_anterior = row;


}
void TabMetodos::on_row_param_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column){

    Gtk::TreeModel::Row row =  *treeModelParametros->get_iter(path);

    if(parametro_seleccionado){
        actualizar_parametro();
    }else{
        activar_entradas_parametros();
    }

    nombreParametro.getEntry()->set_text(row[columnasParametros.nombre]);
    tipoParametro.getEntry()->set_text(row[columnasParametros.tipo]);
    valorParametro.getEntry()->set_text(row[columnasParametros.valor]);

    row_parametro_anterior = row;
    parametro_seleccionado = true;
}

void TabMetodos::actualizar_metodo(){

    std::stringstream temporal;
    std::string firmaOriginal;
    std::string firmaNueva;
    Visibilidad visibilidad;
    std::map<std::string,Metodo*>::iterator it;
    Metodo* metodo;

    if(parametro_seleccionado){
        actualizar_parametro();
    }

    temporal << row_metodo_anterior[columnasMetodos.nombre] << row_metodo_anterior[columnasMetodos.parametros];
    firmaOriginal = temporal.str();

    row_metodo_anterior[columnasMetodos.nombre] = nombreMetodo.getEntry()->get_text();
    row_metodo_anterior[columnasMetodos.tipo] = tipoMetodo.getEntry()->get_text();

    if (metodoPublico.get_active()){
        row_metodo_anterior[columnasMetodos.visibilidad] = Utils::representacionPublico;
        visibilidad = PUBLIC;
    }else if (metodoPrivado.get_active()){
        row_metodo_anterior[columnasMetodos.visibilidad] = Utils::representacionPrivado;
        visibilidad = PRIVATE;
    }else{
        row_metodo_anterior[columnasMetodos.visibilidad] = Utils::representacionProtegido;
        visibilidad = PROTECTED;
    }



    it = metodos.find(firmaOriginal);

    if(it != metodos.end())
    {
        metodo = it->second;
        metodos.erase(it);

        metodo->setNombre(nombreMetodo.getEntry()->get_text());
        metodo->setTipo(tipoMetodo.getEntry()->get_text());
        metodo->setVisibilidad(visibilidad);

    }else{
        metodo = new Metodo(tipoMetodo.getEntry()->get_text(),visibilidad,
                nombreMetodo.getEntry()->get_text());
    }


    modificar_parametros_en_metodo(metodo);

    row_metodo_anterior[columnasMetodos.parametros] = generarListaParametros(metodo->getParametros());

    temporal.str(std::string());//Borro lo que tenga el string stream
    temporal << row_metodo_anterior[columnasMetodos.nombre] << row_metodo_anterior[columnasMetodos.parametros];
    firmaNueva = temporal.str();


    metodos[firmaNueva] = metodo;

    parametro_seleccionado = false;
    desactivar_entradas_parametros();

}
void TabMetodos::actualizar_parametro(){

    row_parametro_anterior[columnasParametros.nombre] = nombreParametro.getEntry()->get_text();
    row_parametro_anterior[columnasParametros.tipo] = tipoParametro.getEntry()->get_text();
    row_parametro_anterior[columnasParametros.valor] = valorParametro.getEntry()->get_text();

}

void TabMetodos::modificar_parametros_en_metodo(Metodo* metodo){
    std::stringstream ss;
    std::vector<Parametro*> parametros_nuevos;
    Parametro* parametro;

    std::string tipo;
    std::string nombre;
    std::string valor;

    Gtk::TreeModel::Children children = treeModelParametros->children();

    for(Gtk::TreeModel::Children::iterator iter = children.begin();iter != children.end(); ++iter)
    {
        Gtk::TreeModel::Row row = *iter;

        ss.str(std::string());//Borro lo que tenga el string stream
        ss << row[columnasParametros.tipo];
        tipo = ss.str();

        ss.str(std::string());//Borro lo que tenga el string stream
        ss << row[columnasParametros.nombre];
        nombre = ss.str();

        ss.str(std::string());//Borro lo que tenga el string stream
        ss << row[columnasParametros.valor];
        valor = ss.str();

        parametro = new Parametro(tipo,nombre,valor);
        parametros_nuevos.push_back(parametro);
    }

    metodo->remplazarParametros(parametros_nuevos);
}

void TabMetodos::on_button_NuevoMetodo_clicked(){

    Visibilidad visibilidad = PUBLIC;
    std::stringstream nombreNuevo;
    std::stringstream temporal;
    std::string tipoNuevo = "void";
    std::string firmaNueva;
    Metodo* metodo;


    nombreNuevo << "metodo_" << numeroMetodo;
    numeroMetodo++;

    Gtk::TreeModel::Row row = *(treeModelMetodos->append());
    row[columnasMetodos.nombre] = nombreNuevo.str();
    row[columnasMetodos.tipo] = tipoNuevo;
    row[columnasMetodos.visibilidad] = Utils::visivilidadEnumToString[visibilidad];
    row[columnasMetodos.parametros] = "( )";

    metodo = new Metodo(tipoNuevo,visibilidad,nombreNuevo.str());


    temporal << row[columnasMetodos.nombre] << row[columnasMetodos.parametros];
    firmaNueva = temporal.str();


    metodos[firmaNueva] = metodo;

    if(metodo_seleccionado){
        actualizar_metodo();
    }

    metodo_seleccionado = false;
    desactivar_entradas_metodos();


}
void TabMetodos::on_button_NuevoParametro_clicked(){
    std::stringstream nombreNuevo;
    std::string tipoNuevo = "void";
    std::string valorNuevo = "";

    nombreNuevo << "parametro_" << numeroParametro;
    numeroParametro++;

    Gtk::TreeModel::Row row = *(treeModelParametros->append());
    row[columnasParametros.nombre] = nombreNuevo.str();
    row[columnasParametros.tipo] = tipoNuevo;
    row[columnasParametros.valor] = valorNuevo;

    if(parametro_seleccionado){
        actualizar_parametro();
    }
    parametro_seleccionado = false;
    desactivar_entradas_parametros();
}

void TabMetodos::on_button_EliminarMetodo_clicked(){
    std::stringstream temporal;
    std::map<std::string,Metodo*>::iterator it;

    if(metodo_seleccionado){
        desactivar_entradas_metodos();

        temporal << row_metodo_anterior[columnasMetodos.nombre] << row_metodo_anterior[columnasMetodos.parametros];
        it = metodos.find(temporal.str());
        if(it != metodos.end()){
            delete it->second;
            metodos.erase(it);
        }

        treeModelMetodos->erase(row_metodo_anterior);
        metodo_seleccionado = false;
        parametro_seleccionado= false;
        treeModelParametros->clear();
    }
}
void TabMetodos::on_button_EliminarParametro_clicked(){
    if(parametro_seleccionado){
        desactivar_entradas_parametros();
        treeModelParametros->erase(row_parametro_anterior);
        parametro_seleccionado= false;
    }
}

void TabMetodos::desactivar_entradas_metodos(){
    metodoPublico.set_sensitive(false);
    metodoPrivado.set_sensitive(false);
    metodoProtegido.set_sensitive(false);

    nombreMetodo.getEntry()->set_sensitive(false);
    tipoMetodo.getEntry()->set_sensitive(false);
    nombreMetodo.getEntry()->set_text("");
    tipoMetodo.getEntry()->set_text("");

    nuevoParametro.set_sensitive(false);
    eliminarParametro.set_sensitive(false);

    desactivar_entradas_parametros();
}
void TabMetodos::activar_entradas_metodos(){
    metodoPublico.set_sensitive(true);
    metodoPrivado.set_sensitive(true);
    metodoProtegido.set_sensitive(true);

    nombreMetodo.getEntry()->set_sensitive(true);
    tipoMetodo.getEntry()->set_sensitive(true);

    nuevoParametro.set_sensitive(true);
    eliminarParametro.set_sensitive(true);
}
void TabMetodos::desactivar_entradas_parametros(){
    nombreParametro.getEntry()->set_sensitive(false);
    tipoParametro.getEntry()->set_sensitive(false);
    valorParametro.getEntry()->set_sensitive(false);

    nombreParametro.getEntry()->set_text("");
    tipoParametro.getEntry()->set_text("");
    valorParametro.getEntry()->set_text("");

}
void TabMetodos::activar_entradas_parametros(){
    nombreParametro.getEntry()->set_sensitive(true);
    tipoParametro.getEntry()->set_sensitive(true);
    valorParametro.getEntry()->set_sensitive(true);
}

std::vector<Metodo*> TabMetodos::getMetodos(){
    std::map<std::string,Metodo*>::iterator it;
    std::vector<Metodo*> temporal;

    //Para guardar los cambios actuales si se preciona aceptar
    if(metodo_seleccionado){
        actualizar_metodo();
    }

    for(it = metodos.begin();it != metodos.end();it++){
        temporal.push_back(it->second);
    }

    eliminarMetodos = false;

    return temporal;
}
